﻿using System.Buffers;
using System.Collections;

namespace 历史积累
{
    /// <summary>
    /// 保存最近的某几条记录
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DynamicRecord<T> : IEnumerable
    {
        private struct Record
        {
            public Record()
            {
                Value = default;
                Time = null;
            }

            /// <summary>
            /// 存储数据
            /// </summary>
            public T? Value { get; set; }

            /// <summary>
            /// 过期时间
            /// </summary>
            public DateTime? Time { get; set; }
        }

        private TimeSpan? AbsoluteExpirationRelativeToNow { get; }

        public DynamicRecord(int len, TimeSpan? absoluteExpirationRelativeToNow = null)
        {
            Len = len;
            Data = new Record[Len];
            AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow;
        }

        public int Len { get; }
        private Record[] Data { get; }
        private int Index { get; set; }
        private bool Recycle { get; set; }

        public void Add(T value, TimeSpan? absoluteExpirationRelativeToNow = null)
        {
            lock (Data)
            {
                Data[Index].Value = value;
                if (!absoluteExpirationRelativeToNow.HasValue) absoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNow;
                if (absoluteExpirationRelativeToNow.HasValue) Data[Index].Time = DateTime.Now.Add(absoluteExpirationRelativeToNow.Value);
                else Data[Index].Time = null;
                if (++Index >= Len)
                {
                    if (!Recycle) Recycle = true;
                    Index = 0;
                }
            }
        }

        public void Reset()
        {
            lock (Data)
            {
                Array.Clear(Data, 0, Len);
            }
        }

        public IEnumerator GetEnumerator()
        {
            int index;
            bool rec;
            Record[] data = ArrayPool<Record>.Shared.Rent(Len);
            try
            {
                lock (this)
                {
                    Array.Copy(Data, data, Len);
                    index = Index;
                    rec = Recycle;
                }
                var time = DateTime.Now;
                for (int i = 0; i < Len; i++)
                {
                    var item = i;
                    if (rec) item += index;
                    if (item >= Len) item -= Len;
                    var result = data[item];
                    if (result.Value is not null && (!result.Time.HasValue || result.Time >= time))
                        yield return result.Value;
                    else yield return default(T);
                }
            }
            finally
            {
                ArrayPool<Record>.Shared.Return(data);
            }
        }
    }
}